package models.bill;

import models.UUIDModel;
import models.admin.AdminUser;
import models.constants.DeletedStatus;
import models.contract.BankCard;
import models.contract.Contract;
import models.enums.BillType;
import models.enums.PaymentStatus;
import models.enums.VoucherStatus;
import models.user.User;
import org.hibernate.SQLQuery;
import org.hibernate.transform.Transformers;
import play.Logger;
import play.db.jpa.JPA;
import util.common.DateUtil;
import utils.WxCpMsgUtil;
import utils.WxMsgTempUtil;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 账单
 */
@Entity
@Table(name = "bl_bills")
public class Bill extends UUIDModel {

    /**
     * 账单编号
     */
    @Column(name="code")
    public String code;

    /**
     * 接收账单的人
     */
    @JoinColumn(name = "user_id")
    @ManyToOne
    public User user;

    /**
     * 账单类型 :月账单
     */
    @Enumerated(EnumType.STRING)
    @Column(name="bill_type", length = 10)
    public BillType billType;

    /**
     * 账单开始时间
     * 2016-12-01 00:00:00
     */
    @Column(name="begin_day", length = 20)
    public String beginDay;

    /**
     * 账单结束时间
     * 2016-12-31 23:59:59
     */
    @Column(name="end_day", length = 20)
    public String endDay;

    /**
     * 合同信息
     */
    @JoinColumn(name = "contract_id")
    @ManyToOne
    public Contract contract;

    /**
     * 账单标题
     */
    @Column(name = "title", length = 30)
    public String title;

    /**
     * 订单金额
     */
    @Column(name = "amount")
    public BigDecimal amount;

    /**
     * 对应支付银行卡.
     */
    @JoinColumn(name = "bank_card_id")
    @ManyToOne
    public BankCard bankCard;

    /**
     * 用户查看时候.
     *
     * TODO: 推送给用户的时间.
     */
    @Column(name = "user_view_at")
    public Date userViewAt;

    /**
     *  财务确认人
     */
    @JoinColumn(name = "finance_staff")
    @ManyToOne
    public AdminUser financeStaff;

    /**
     * 确认时间
     */
    @Column(name="finance_confirm_at")
    public Date financeConfirmAt;

    /**
     * 支付状态
     */
    @Enumerated(EnumType.STRING)
    @Column(name="payment_status", length = 10)
    public PaymentStatus paymentStatus;

    /**
     * 最后支付时间
     */
    @Column(name="last_payment_at")
    public Date lastPaymentAt;

    /**
     * 根据BillId查询
     * @param id
     * @return
     */
    public static Bill findByBillId(String id){
        return Bill.find(" id = ? and deleted = ?" , id , DeletedStatus.UN_DELETED).first();
    }

    /**
     * 根据用户查询账单
     * @param user
     * @return
     */
    public static List<Bill> findByUser(User user){
//        return Bill.find("user = ? order by createdAt desc" , user).fetch(5);
        return Bill.find("order by createdAt desc").fetch(5);
    }

    /**
     * 根据用户信息查询账单
     * @param userId
     * @return
     */
    public static List<Map<String, Object>> findPaymenListByUserId(String userId) {
        String execSql = "select t1.id,t1.title,'bill' as type, "+
                " (select count(1) from bl_bill_payments t2 where t2.bill_id = t1.id and t2.deleted = 0) as is_pay," +
                " t1.amount,t1.payment_status" +
                " from bl_bills t1" +
                " left join co_contracts t2 on t1.contract_id = t2.id where t1.payment_status is not null ";
        String whereSql = " and  EXISTS(select 1 from co_contract_card_relations i1 where t2.id = i1.contract_id and user_id = '" + userId + "')";
        Query query=em().createNativeQuery(execSql+whereSql);
        query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        List<Map<String,Object>> listData=query.getResultList();
        return listData;
    }

    /**
     * 将所有未支付或者支付失败的账单改为逾期
     */
    public static void overdueBills(String billBeganAt){
        String sqlUpdate = "update bl_bills a set a.payment_status = 'OVERDUE' where  a.deleted = 0 and (a.payment_status = 'NOT_PAID'  or a.payment_status = 'FAILURE') and  a.begin_day  < '"+billBeganAt+"'";
        JPA.em().createNativeQuery(sqlUpdate).executeUpdate();
    }

    /**
     * 检查合同是否生成账单
     * 账单开始时间是上个月的开始时间
     * @param contract
     * @param beginDay
     * @return
     */
    public static Boolean checkGenerateBill(Contract contract , String beginDay){
        //如果账单开始月份已经存在,则不再重复生成账单
        return Bill.count("contract = ? and deleted = ? and beginDay = ? " , contract , DeletedStatus.UN_DELETED , beginDay) == 0;
    }


    //自动 生成账单
    public static String generateBill(){

        String message = "";
        Date today = DateUtil.getTomorrow(); // 当前日期 时间:00:00:00 演示用
        // 账单开始日期
        Date billBeganDate = DateUtil.firstDayOfMonth();//当月第一天
//        Date billBeganDate = DateUtil.getBeforeMonth(today ,-1); //获取指定日期往前推一个月,为开始日期
//        Date billBeganDate = DateUtil.lastMonthOfFirstDay();
        String billBeganAt = DateUtil.dateToString(billBeganDate, "yyyy-MM-dd HH:mm:ss");
        // 账单结束日期
//        Date monthBegan = DateUtil.firstDayOfMonth();//当月第一天
        String billEndAt = DateUtil.dateToString(today, "yyyy-MM-dd HH:mm:ss");
        //所属月份,账单, 凭证的所属月份
        String belongMonth = DateUtil.dateToString(billBeganDate , "yyyy-MM");

        Logger.info("开始时间和结束时间--------------------------------beganAt : %s | endAt : %s " ,billBeganAt , billEndAt);

        //将支付状态为未支付和支付失败的账单 改为逾期状态
//        Bill.overdueBills(billBeganAt);
        Bill.overdueBills(billBeganAt);

        //查询本月之前所有生成凭证,且凭证为未登账或者已登账状态的合同
        //演示状态下以现在时间为节点
        String sqlSelect = "select a.contract_id as contractId from bl_vouchers a where (a.voucher_status = 'UN_BILL'  or a.voucher_status = 'BILLED') and a.created_at < '" + billEndAt + "' and a.deleted = 0 group by a.contract_id ;";
        Query query = Contract.em().createNativeQuery(sqlSelect);
        query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        List<Map<String, String>> contractIdList = query.getResultList();

        Logger.info("-----合同数量:%s", contractIdList.size());
        if (contractIdList != null && contractIdList.size() > 0) {
            long billNum = 0l;
            for (Map<String, String> map : contractIdList) {
                String contractId = map.get("contractId");
                Contract contract = Contract.findByContractId(contractId);
                //生成账单明细
                List<Voucher> voucherList = Voucher.loadByContractIdAndNOPAYED(contractId ,today);
                if ( Bill.checkGenerateBill(contract,billBeganAt) == true && voucherList != null && voucherList.size() > 0) {
                    Logger.info("合同:%s | 凭证数量:%s", contractId, voucherList.size());
                    BankCard bankCard = BankCard.findByContract(contract);
                    User user = null;
                    if (bankCard != null && bankCard.user != null)
                        user = bankCard.user;

                    Bill bill = new Bill();
                    bill.loadKey();
                    bill.deleted = DeletedStatus.UN_DELETED;
                    bill.createdAt = new Date();
                    bill.bankCard = bankCard;
                    bill.user = user; // 接收人取合同绑定的银行卡的user
                    bill.billType = BillType.MONTH;
                    //账单开始时间和结束时间分别取的上月第一天和最后一天的日期
                    bill.beginDay = billBeganAt;
                    bill.endDay = billEndAt;
                    bill.contract = contract;
                    bill.title = DateUtil.dateToString(billBeganDate, "yyyy年MM月") + "账单";
                    bill.userViewAt = DateUtil.stringToDate(DateUtil.dateToString(new Date(), "yyyy-MM") + "-05 09:00:00", "yyyy-MM-dd HH:mm:ss");
                    bill.paymentStatus = PaymentStatus.NOT_PAID;
                    bill.belongCompany = contract.belongCompany != null ? contract.belongCompany : null ;
                    bill.save();

                    BigDecimal totalAmount = new BigDecimal(0);
                    for (Voucher voucher : voucherList) {
                        BillDetail billDetail = new BillDetail();
                        billDetail.bill = bill;
                        billDetail.remark = voucher.remark;
                        billDetail.amount = voucher.amount;
                        billDetail.feeType = voucher.feeType;
                        billDetail.deleted = DeletedStatus.UN_DELETED;
                        billDetail.createdAt = new Date();
                        billDetail.belongCompany = bill.belongCompany != null ? bill.belongCompany : null ;
                        billDetail.save();
                        totalAmount = totalAmount.add(billDetail.amount);
                        //如果凭证的所属月份不属于账单月,该凭证改为逾期凭证,并重新生成凭证
//                        if (!voucher.belongMonth.equals(belongMonth)) {
                        //演示时凭证都是当前时间生成的,所以取的时间节点是当前时间
                        if (!voucher.belongMonth.equals(DateUtil.dateToString(today,"yyyy-MM"))) {
                            voucher.voucherStatus = VoucherStatus.OVERDUE;//凭证逾期
                            voucher.save();

                            Voucher newVoucher = new Voucher();
                            newVoucher.createdAt = billBeganDate;
                            newVoucher.deleted = DeletedStatus.UN_DELETED;
                            newVoucher.contract = voucher.contract;
                            newVoucher.user = voucher.user;
                            newVoucher.adminUser = voucher.adminUser;
                            newVoucher.voucherStatus = VoucherStatus.BILLED;
                            newVoucher.feeType = voucher.feeType;
                            newVoucher.amount = voucher.amount;
                            newVoucher.businessRecordId = voucher.businessRecordId;
                            newVoucher.remark = voucher.belongMonth+"逾期"+" :" + (voucher.remark != null ? voucher.remark : "");
                            newVoucher.billDetail = billDetail;
                            newVoucher.belongMonth = voucher.belongMonth;
                            newVoucher.belongCompany = voucher.belongCompany != null ? voucher.belongCompany : null ;
                            newVoucher.save();
                        } else {
                            voucher.voucherStatus = VoucherStatus.BILLED;// 已登账
                            voucher.billDetail = billDetail;
                            voucher.save();
                        }

                    }
                    bill.amount = totalAmount;
                    bill.save();
                    billNum += 1;


                }
            }
            WxCpMsgUtil.sendBillMsgToFinance(billNum);
            message = "本次共生成: "+billNum+"份账单";
            Logger.info(message);
        } else {
            message = " 目前没有账单可以生成.";
            Logger.info(message);
        }
        return message ;
    }

    //查询当月生成的账单
    public static List<Bill> getThisMonthBillList(){
        Date monthOfFirstDay = DateUtil.firstDayOfMonth();
        return Bill.find(" deleted = ? and createdAt >= ?" , DeletedStatus.UN_DELETED , monthOfFirstDay).fetch();
    }

    //查询当月未支付或者支付失败的账单
    public static List<Bill> getUnPayBill(){
        Date monthOfFirstDay = DateUtil.firstDayOfMonth();
        return Bill.find(" deleted = ? and createdAt >= ? and paymentStatus != ?  and paymentStatus != ?" , DeletedStatus.UN_DELETED , monthOfFirstDay , PaymentStatus.OVERDUE , PaymentStatus.SUCCESS).fetch();
    }

    //账单发送给客户
    public static void sendBillDetailToCustom(){
        String thisMonth = DateUtil.dateToString(new Date(),"yyyy年MM月");
        List<Bill> billList = Bill.getUnPayBill();
        if(billList != null && billList.size() >0){
            for(Bill bill : billList){
                if(bill.user != null){
                    WxMsgTempUtil.sendBillDetailToCustom(bill);
                    Logger.info("--------------------账单ID:"+bill+"推送成功");
                }else
                    Logger.info("--------------------账单ID:"+bill+"没有接收用户,推送失败");
            }
            Logger.info("--------------------账单发送结束----------------------------");
        }else {
            Logger.info("-----------------"+thisMonth+"没有可以发送的账单------------------");
        }
    }


}
